Program to create bootcache area Changes to bootcache program to match changes that have been made to the bootcache device mapper. BUG=chromium-os:25441 TEST=created bootcache and booted system. Change-Id: Idc62d4cea72d69f4c202460400219184916231ee Reviewed-on: https://gerrit.chromium.org/gerrit/31761 Reviewed-by: Luigi Semenzato <semenzato@chromium.org> Commit-Ready: Paul Taysom <taysom@chromium.org> Tested-by: Paul Taysom <taysom@chromium.org> 
diff --git a/bootcache.c b/bootcache.c index 515bde2..5fa9a8f 100644 --- a/bootcache.c +++ b/bootcache.c 
@@ -12,7 +12,10 @@  * conjunction with dm-bootcache device mapper to coalesce  * the blocks used during boot.  * - * bootcache [-t] <device-name> + * Sizes and offsets are measured in 512 byte sectors. + * Space is allocated in CHUNK_SIZE chunks. + * + * bootcache [-t] <device-name> <raw-partition>  *  * -t - for testing - looks in a different place for  * information files. @@ -21,14 +24,14 @@  * prefix.  *  * Files: - * 1. Device - /dev/dm-0 - Where the blocks to be cached - * are stored. Both the original and cached - * copy. + * 1. Device - <raw-partition> - Where the blocks to be + * cached are stored. Both the original and + * cached copy.  * 2. Header - /sys/kernel/debug/dm-bootcache/dm-0/header  * Header for the boot cache. It contains the  * information the bootcache utility will need  * to create the bootcache. - * 3. Trace - /sys/kernel/debug/dm-bootcache/dm-0/trace + * 3. Trace - /sys/kernel/debug/dm-bootcache/dm-0/blocktrace  * Trace of files read during boot  * 4. Valid - /sys/kernel/debug/dm-bootcache/dm-0/valid  * Returns "1" if cache is valid @@ -41,14 +44,16 @@    #include <sys/types.h>  #include <sys/stat.h> +#include <sys/user.h>  #include <errno.h>  #include <fcntl.h>  #include <stdarg.h>  #include <stdbool.h> -#include <stdlib.h>  #include <stdint.h>  #include <stdio.h> +#include <stdlib.h>  #include <string.h> +#include <syslog.h>  #include <unistd.h>    #include "dm-bootcache.h" @@ -56,30 +61,41 @@  typedef uint64_t u64;  typedef uint32_t u32;   -#define MAX_BLOCKS 128 +#define CHUNK_SIZE 4096	/* todo(taysom) should get from bootcache_hdr */ +#define SECTOR_SHIFT 9 +#define MAX_CHUNKS 128  #define MAX_FILE_NAME 256 +#define SECTORS_PER_CHUNK (CHUNK_SIZE >> SECTOR_SHIFT) +#define MAX_MSG 1024   -static struct Bootcache_hdr Header; +static struct bootcache_hdr Header;  static struct { -	struct Trace *tr; +	struct bootcache_trace *tr; 	int num;  } Trace;    static const char Progname[] = "bootcache";   -static char Device_file[MAX_FILE_NAME];  static char Valid_file[MAX_FILE_NAME];  static char Free_file[MAX_FILE_NAME];  static char Header_file[MAX_FILE_NAME]; -static char Trace_file[MAX_FILE_NAME]; +static char Blocktrace_file[MAX_FILE_NAME];   -static u64 Header_block;  static u64 Trace_start;  static u64 Cache_start;    #define fatal(fmt, ...)	pr_fatal(__FILE__, __FUNCTION__, __LINE__, \ 	fmt, ## __VA_ARGS__)   +#define PRs(_x) printf("|%s<%d> %s %s\n", __FUNCTION__, __LINE__, \ +	# _x, _x); + +#define PRd(_x) printf("|%s<%d> %s %lld\n", __FUNCTION__, __LINE__, \ +	# _x, (unsigned long long)(_x)); + +#define PRx(_x) printf("|%s<%d> %s %llx\n", __FUNCTION__, __LINE__, \ +	# _x, (unsigned long long)(_x)); +  /* pr_fatal: print error message and exit */  static void pr_fatal( 	const char *file, @@ -87,20 +103,28 @@ 	int line, 	const char *fmt, ...)  { +	char msg[MAX_MSG]; 	va_list args; +	int n = MAX_MSG; +	int i = 0; +	int r;   	fflush(stdout); -	fprintf(stderr, "Fatal %s %s:%s<%d> ", Progname, file, func, line); -	if (fmt) { +	r = snprintf(msg, n, "Fatal %s %s:%s<%d> ", Progname, file, func, line); +	n -= r; +	i += r; +	if (n && fmt) { 	va_start(args, fmt); -	vfprintf(stderr, fmt, args); +	r = vsnprintf(&msg[i], n, fmt, args); +	n -= r; +	i += r; 	va_end(args);   -	if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') { -	fprintf(stderr, " %s<%d>", strerror(errno), errno); +	if (n && fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') { +	snprintf(&msg[i], n, " %s<%d>", strerror(errno), errno); 	} 	} -	fprintf(stderr, "\n"); +	syslog(LOG_ERR, "%s\n", msg); 	exit(2); /* conventional value for failed execution */  }   @@ -153,14 +177,14 @@ 	void *buf; 	int rc;   -	rc = posix_memalign(&buf, BLK_SIZE, npages * BLK_SIZE); +	rc = posix_memalign(&buf, CHUNK_SIZE, npages * CHUNK_SIZE); 	if (rc) { 	fatal("posix_memalign rc=%d", rc); 	} 	return buf;  }   -static u64 num_blocks_in_cache(void) +static u64 num_sectors_in_cache(void)  { 	int i; 	u64 sum = 0; @@ -171,33 +195,41 @@ 	return sum;  }   -static void compute_sections(void) +static u64 num_meta_sectors(void)  { -	Header.num_blks_meta = BLK_ALIGN(Trace.num * sizeof(*Trace.tr)); -	Header.num_blks_data = num_blocks_in_cache(); -	Header_block = Header.blkno; -	Trace_start = Header_block + 1; -	Cache_start = Trace_start + Header.num_blks_meta; +	u64 num_bytes = Trace.num * sizeof(*Trace.tr); + +	/* Align to page boundary then convert to sectors */ +	return ((num_bytes + CHUNK_SIZE - 1) / CHUNK_SIZE) * SECTORS_PER_CHUNK;  }   -static void copy_trace(int dst, int src, struct Trace tr, void *buf) +static void compute_sections(void) +{ +	Header.num_trace_recs = Trace.num; +	Header.sectors_meta = num_meta_sectors(); +	Header.sectors_data = num_sectors_in_cache(); +	Trace_start = Header.sector + SECTORS_PER_CHUNK; +	Cache_start = Trace_start + Header.sectors_meta; +} + +static void copy_trace(int dst, int src, struct bootcache_trace tr, void *buf)  { 	u64 n; 	u64 remainder; 	u64 offset; 	int rc;   -	offset = tr.blkno << BLK_SHIFT; -	remainder = tr.count << BLK_SHIFT; -	n = BLK_SIZE * MAX_BLOCKS; +	offset = tr.sector << SECTOR_SHIFT; +	remainder = tr.count << SECTOR_SHIFT; +	n = MAX_CHUNKS * CHUNK_SIZE; 	while (remainder) { 	if (n > remainder) { 	n = remainder; 	} 	rc = pread(src, buf, n, offset); 	if (rc < 0) { -	fatal("pread trace offset=%llu num blocks=%llu", -	offset >> BLK_SHIFT, n >> BLK_SHIFT); +	fatal("pread trace offset=%llu num sectors=%llu:", +	offset >> SECTOR_SHIFT, n >> SECTOR_SHIFT); 	} 	if (rc != n) { 	fatal("pread read only %u bytes expected %llu", @@ -205,8 +237,8 @@ 	} 	rc = write(dst, buf, n); 	if (rc < 0) { -	fatal("write trace offset=%llu num blocks=%llu", -	offset >> BLK_SHIFT, n >> BLK_SHIFT); +	fatal("write trace offset=%llu num sectors=%llu:", +	offset >> SECTOR_SHIFT, n >> SECTOR_SHIFT); 	} 	if (rc != n) { 	fatal("write wrote only %u bytes expected %llu", @@ -224,8 +256,9 @@   	int src = open(device, O_RDONLY); 	int dst = open(device, O_WRONLY); -	void *buf = malloc_buf(MAX_BLOCKS); -	rc = lseek(dst, Cache_start << BLK_SHIFT, SEEK_SET); +	void *buf = malloc_buf(MAX_CHUNKS); + +	rc = lseek(dst, Cache_start << SECTOR_SHIFT, SEEK_SET); 	if (rc == -1) { 	fatal("lseek for cache start:"); 	} @@ -238,28 +271,31 @@ 	eclose(src);  }   -static void dump_trace(struct Trace *tr, int num_recs) +static void dump_trace()  { +	struct bootcache_trace *tr = Trace.tr; 	int i;   -	for (i = 0; i < num_recs; i++, tr++) { -	printf("%llu %llu %llu\n", tr->blkno, tr->count, tr->ino); +	if (0) { +	for (i = 0; i < Trace.num; i++, tr++) { +	printf("%llu %llu %llu\n", tr->sector, tr->count, tr->ino); +	} 	}  }    /* - * Because we are reading a pseudo file, we scan it to - * see how big it is. + * Because we are reading a pseudo file in sysfs, + * we scan it to see how big it is.  */ -static u64 num_traces(const char *file) +static u64 num_bytes(const char *file)  { -	struct Trace trace[1024]; +	char buf[CHUNK_SIZE]; 	ssize_t rc; 	u64 sum = 0;   	int fd = eopen(file, O_RDONLY); 	for (;;) { -	rc = read(fd, trace, sizeof(trace)); +	rc = read(fd, buf, sizeof(buf)); 	if (rc == -1) 	fatal("read %s:", file); 	if (rc == 0) @@ -267,23 +303,39 @@ 	sum += rc; 	} 	eclose(fd); -	return sum / sizeof(struct Trace); +	return sum;  }    static void read_trace(const char *file)  { -	u64 n = num_traces(file); +	/* + * Because this is a sysfs file, we have to read it to get + * its size. Even if more data is appended to the file, we + * don't care, we just want the data up to this point in + * time. + */ +	u64 n = num_bytes(file); 	ssize_t rc; 	int fd; +	char *b;   -	Trace.tr = emalloc(n * sizeof(struct Trace)); -	Trace.num = n; +	Trace.tr = emalloc(n); +	Trace.num = n / sizeof(struct bootcache_trace); 	fd = eopen(file, O_RDONLY); -	rc = read(fd, Trace.tr, n * sizeof(struct Trace)); -	if (rc == -1) { -	fatal("read %s:", file); +	/* + * Because sysfs only returns a page at a time, + * will need to do the read in a loop. + */ +	for (b = (char *)Trace.tr; n; n -= rc, b += rc) { +	rc = read(fd, b, n); +	if (rc == -1) { +	fatal("read %s:", file); +	} +	if (rc == 0) { +	fatal("trying to read %lld bytes", n); +	} 	} -	dump_trace(Trace.tr, n); +	dump_trace(); 	eclose(fd);  }   @@ -319,7 +371,7 @@ 	int rc;   	fd = eopen(file, O_WRONLY); -	rc = pwrite(fd, &Header, sizeof(Header), Header_block << BLK_SHIFT); +	rc = pwrite(fd, &Header, sizeof(Header), Header.sector << SECTOR_SHIFT); 	if (rc != sizeof(Header)) { 	fatal("pwrite %s rc=%d:", file, rc); 	} @@ -334,7 +386,7 @@ 	ssize_t size = Trace.num * sizeof(*Trace.tr);   	fd = eopen(file, O_WRONLY); -	rc = pwrite(fd, Trace.tr, size, Trace_start); +	rc = pwrite(fd, Trace.tr, size, Trace_start << SECTOR_SHIFT); 	if (rc != size) { 	fatal("pwrite %s rc=%ld size=%ld:", file, rc, size); 	} @@ -380,96 +432,77 @@ 	return buf[0] == '1';  }   -static void gen_file_name(char *file_name, int size, -	const char *fmt, const char *name) +static void gen_file_name(char *file_name, int size, const char *fmt, +	const char *prefix, const char *name)  { 	int rc;   -	rc = snprintf(file_name, size, fmt, name); +	rc = snprintf(file_name, size, fmt, prefix, name); 	if (rc >= size) { 	fatal("Name too long %s", name); 	}  }   -static void gen_file_names(const char *name) +static void gen_file_names(const char *fmt, const char *device_mapper)  { -	gen_file_name(Device_file, sizeof(Device_file), "/dev/%s", name); 	gen_file_name(Valid_file, sizeof(Valid_file), -	"/sys/kernel/debug/dm-bootcache/%s/valid", name); +	fmt, device_mapper, "valid"); 	gen_file_name(Free_file, sizeof(Free_file), -	"/sys/kernel/debug/dm-bootcache/%s/free", name); +	fmt, device_mapper, "free"); 	gen_file_name(Header_file, sizeof(Header_file), -	"/sys/kernel/debug/dm-bootcache/%s/header", name); -	gen_file_name(Trace_file, sizeof(Trace_file), -	"/sys/kernel/debug/dm-bootcache/%s/trace", name); -} - -static void test_file_names(const char *name) -{ -	gen_file_name(Device_file, sizeof(Device_file), -	"/tmp/%s/dev", name); -	gen_file_name(Valid_file, sizeof(Valid_file), -	"/tmp/%s/valid", name); -	gen_file_name(Free_file, sizeof(Free_file), -	"/tmp/%s/free", name); -	gen_file_name(Header_file, sizeof(Header_file), -	"/tmp/%s/header", name); -	gen_file_name(Trace_file, sizeof(Trace_file), -	"/tmp/%s/trace", name); +	fmt, device_mapper, "header"); +	gen_file_name(Blocktrace_file, sizeof(Blocktrace_file), +	fmt, device_mapper, "blocktrace");  }    static void usage(void)  { -	fprintf(stderr, "Usage: %s [-t] <name>\n" -	" e.g %s dm-0\n", +	fprintf(stderr, "Usage: %s [-t]" +	" <device mapper> <raw partition>\n" +	" e.g %s dm-0 /dev/sda3\n", 	Progname, Progname); 	exit(2);  }    int main(int argc, char *argv[])  { -	bool test = false; -	char *name = NULL; +	char *device_mapper = NULL; +	char *raw_partition = NULL;   +	openlog(Progname, LOG_PERROR | LOG_CONS | LOG_PID, 0); +	syslog(LOG_ERR, "started\n"); 	for (;;) { 	int c;   -	c = getopt(argc, argv, "t?"); +	c = getopt(argc, argv, "?"); 	if (c == -1) 	break; 	switch (c) { -	case 't': -	test = true; -	break; 	case '?': 	default: 	usage(); 	break; 	} 	} -	if (optind >= argc) { +	if (optind+2 != argc) { 	usage(); 	} -	name = argv[optind]; -	if (test) { -	test_file_names(name); -	} else { -	gen_file_names(name); -	} -	if (is_valid(Valid_file)) { -	/* Because the boot cache is valid, the block - * traces are not kept so the boot cache can't - * be rebuilt. To force a rebuild of the cache, - * zero the header. +	device_mapper = argv[optind]; +	raw_partition = argv[optind + 1]; +	gen_file_names("/sys/devices/virtual/block/%s/dm/%s", +	device_mapper); +	if (!is_valid(Valid_file)) { +	/* + * Rebuild the bootcache  */ -	return 0; +	read_header(Header_file); +	read_trace(Blocktrace_file); +	compute_sections(); +	copy_blocks(raw_partition); +	write_trace(raw_partition); +	write_header(raw_partition); 	} -	read_header(Header_file); -	read_trace(Trace_file); -	compute_sections(); -	copy_blocks(Device_file); -	write_trace(Device_file); -	write_header(Device_file); 	free_bootcache(Free_file); +	syslog(LOG_ERR, "done\n"); 	return 0;  } 
diff --git a/bootcache.conf b/bootcache.conf new file mode 100644 index 0000000..27d00db --- /dev/null +++ b/bootcache.conf 
@@ -0,0 +1,19 @@ +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +description "Start the bootcache process" +author "chromium-os-dev@chromium.org" + +# Starts the bootcache deamon that either creates the bootcache +# or cleans it up after it is used. +start on started system-services + +pre-start script + sleep 5 # Give a little extra time for chrome +end script + +script + device=$(rootdev -s) + exec ionice -c3 bootcache dm-0 $device +end script 
diff --git a/dm-bootcache.h b/dm-bootcache.h index af12df8..536911f 100644 --- a/dm-bootcache.h +++ b/dm-bootcache.h 
@@ -1,42 +1,44 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. +/* + * Copyright 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details.  */   -#ifndef _BOOTCACHE_H -#define _BOOTCACHE_H +#ifndef DM_BOOTCACHE_H +#define DM_BOOTCACHE_H    #include <linux/types.h>   -enum {	BLK_SHIFT = 12, -	BLK_SIZE = 1<<BLK_SHIFT, -	BLK_MASK = BLK_SIZE - 1, -	SECTOR_BLOCK_SHIFT = 3, -	BOOTCACHE_MAGIC = 1651470196, +enum {	BOOTCACHE_MAGIC = 1651470196, 	BOOTCACHE_VERSION = 2, 	MAX_SIGNATURE = 256  };   - -#define BLK_ALIGN(_x)	(((_x) + BLK_MASK) >> BLK_SHIFT) - -struct Trace { -	__u64	blkno;	/* Block number for data */ -	__u64	count;	/* Number of blocks in request */ -	__u64	ino;	/* Used for anaysis of traces */ +struct bootcache_trace { +	__u64	sector;	/* Sector offset */ +	__u64	count;	/* Number of blocks traced */ +	__u64	ino;	/* Inode number of file */  };   -struct Bootcache_hdr { -	__u64	blkno;	/* Block number for where header is stored */ +struct bootcache_hdr { +	__u64	sector;	/* Sector offset where header is stored */ 	__u32	magic;	/* Magic number */ 	__u32	version;	/* Verion of boot cache */ 	__u32	state;	/* Curent state */ -	__u32	num_blks_meta;	/* Size of trace data on disk */ -	__u32	num_blks_data;	/* Size of the data area */ -	char	date[12];	/* Date and time bootcache was compiled */ -	char	time[12]; +	__u32	num_trace_recs;	/* Number of trace reords */ +	__u32	sectors_meta;	/* Size of trace data on disk in sectors*/ +	__u32	sectors_data;	/* Size of the data area in sectors*/ 	__u32	max_sectors;	/* Max sectors that can to read */ 	__u32	max_hw_sectors;	/* Max hardware sectore that can be read */ +	char	date[12];	/* Date and time dm-bootcache was compiled */ +	char	time[12]; 	char	signature[MAX_SIGNATURE];  };